package org.nobject.common.regex;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.nobject.common.exception.UnSupportException;
import org.nobject.common.lang.ClassUtils;
import org.nobject.common.lang.ObjectUtils;
import org.nobject.common.text.WordUtils;

public class ValidatorUtils {
	
	private static Pattern PATTERN_EMAIL 		= Pattern.compile("^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$");// 邮件地址
	private static Pattern PATTERN_TEL 			= Pattern.compile("^([0-9]{3,4}-)?[0-9]{7,8}$");// 固定电话
	private static Pattern PATTERN_MOBILE 		= Pattern.compile("^(\\+86)?0?1\\d{10}$");// 移动电话
	private static Pattern PATTERN_ALPHA 		= Pattern.compile("^[A-Za-z]+$");// 字母
	private static Pattern PATTERN_DIGITAL 		= Pattern.compile("^\\d+$");// 数字
	private static Pattern PATTERN_CHINESE 		= Pattern.compile("^[\\u4E00-\\u9FA5]+$");// 中文
	private static Pattern PATTERN_IDCARD_15 	= Pattern.compile("^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$");// 15位身份证格式
	private static Pattern PATTERN_IDCARD_18 	= Pattern.compile("^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}[\\d|x|X]$");// 18位身份证格式
	private static Pattern PATTERN_IP 			= Pattern.compile("^((00\\d|1?\\d?\\d|(2([0-4]\\d|5[0-5])))\\.){3}(00\\d|1?\\d?\\d|(2([0-4]\\d|5[0-5])))$");// IP格式
	private static Pattern PATTERN_TIME 		= Pattern.compile("((0?[0-9])|([1-2][0-3]))\\:([0-5]?[0-9])(\\:([0-5]?[0-9]))");// 时间格式
	private static Pattern PATTERN_REPEAT 		= Pattern.compile(".*(.).*\\1.*");// 重复字符格式

	/**
	 * 校验email格式
	 * @param email email
	 */
	public static boolean isEmail(String email) {
		if (email == null) return false;
		else return PATTERN_EMAIL.matcher(email).matches();
	}

	/** 
	 * 校验电话
	 * @param telephone 电话号码
	 */
	public static boolean isTel(String telephone) {
		if (telephone == null) return false;
		else return PATTERN_TEL.matcher(telephone).matches();
	}

	/** 
	 * 校验手机
	 * @param mobile 手机号
	 */
	public static boolean isMobile(String mobile) {
		if (mobile == null) return false;
		else return PATTERN_MOBILE.matcher(mobile).matches();
	}

	/** 
	 * isAlpha
	 * @param alpha
	 */
	public static boolean isAlpha(String alpha) {
		if (alpha == null) return false;
		else return PATTERN_ALPHA.matcher(alpha).matches();
	}

	/** 
	 * isDigital 数字
	 * @param digital 数字
	 */
	public static boolean isDigital(String digital) {
		if (digital == null) return false;
		else return PATTERN_DIGITAL.matcher(digital).matches();
	}
	
	/** 
	 * 是否是中文
	 * @param chinese 中文
	 */
	public static boolean isChinese(String chinese) {
		if (chinese == null) return false;
		else return PATTERN_CHINESE.matcher(chinese).matches();
	}

	/** 
	 * isDateTime
	 * @param dateTime
	 */
	public static boolean isDateTime(String dateTime) {
		return isDateTime(dateTime, "-");
	}

	/**
	 * 校验日期时间 月 日 时间 可以不写前面的0 ,例如 2008-9-2 3:9:1
	 * @param dateTime
	 * @param partition 分隔符 这里只支持 /、/、-、空格 四类分隔符，如果是长度大于1，则取第一个字符作为分隔符
	 */
	public static boolean isDateTime(String dateTime, String partition) {  
	    if (dateTime == null || partition == null || "".equals(partition)) return false;  
	    String s = "";  
	    char split = partition.charAt(0);  
	    if (split != '/' && split != '-' && split != ' ')  throw new IllegalArgumentException((new StringBuilder( "partition can not start with '")).append(partition).append("'!").toString());  
	    s = (new StringBuilder(String.valueOf(s))).append(split).toString();  
	
	    StringBuilder part = new StringBuilder( "^((\\d{2}(([02468][048])|([13579][26]))");  
	    part.append(s);  
	    part.append("((((0?[13578]");  
	    part.append(")|(1[02]))");  
	    part.append(s);  
	    part.append("((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[4");  
	    part.append("69])|(11))");  
	    part.append(s);  
	    part.append("((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\\\/");  
	    part .append("s]?((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][1235679])|([1");  
	    part.append("3579][01345789]))");  
	    part.append(s);  
	    part.append("((((0?[13578])|(1[02]))");  
	    part.append(s);  
	    part.append("((");  
	    part.append("0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))");  
	    part.append(s);  
	    part.append("((");  
	    part.append("0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))");  
	    part.append(s);  
	    part.append("((");  
	    part.append("0?[1-9])|([1-2][0-9])|(30)))|(0?2");  
	    part.append(s);  
	    part.append("((0?[1-9])|(1[0-9])|(2[0-8]))))))");  
	    part  
	            .append("(\\s(((0?[0-9])|([1-2][0-3]))\\:([0-5]?[0-9])\\:([0-5]?[0-9])))?");  
	    return Pattern.matches(part.toString(), dateTime);  
	}

	/** 
	 * isDate 
	 * @param date
	 */
	public static boolean isDate(String date) {
		return isDate(date, "-");
	}

	/**
	 * 校验日期 月 日 可以不写前面的0 ,例如 2008-9-2 3:9:1
	 * 
	 * @param date 日期
	 * @param partition  分隔符 这里只支持 /、/、-、空格 四类分隔符，如果是长度大于1，则取第一个字符作为分隔符
	 */
	public static boolean isDate(String date, String partition) {  
	    if (date == null || partition == null || "".equals(partition))	return false;  
	    String s = "";  
	
	    char split = partition.charAt(0);  
	    if (split != '/' && split != '-' && split != ' ')  throw new IllegalArgumentException((new StringBuilder("partition can not start with '")).append(partition).append("'!").toString());  
	    s = (new StringBuilder(String.valueOf(s))).append(split).toString();  
	
	    StringBuilder part = new StringBuilder("^((//d{2}(([02468][048])|([13579][26]))");  
	    part.append(s);  
	    part.append("((((0?[13578]");  
	    part.append(")|(1[02]))");  
	    part.append(s);  
	    part.append("((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[4");  
	    part.append("69])|(11))");  
	    part.append(s);  
	    part.append("((0?[1-9])|([1-2][0-9])|(30)))|(0?2[//-/////");  
	    part.append("s]?((0?[1-9])|([1-2][0-9])))))|(//d{2}(([02468][1235679])|([1");  
	    part.append("3579][01345789]))");  
	    part.append(s);  
	    part.append("((((0?[13578])|(1[02]))");  
	    part.append(s);  
	    part.append("((");  
	    part.append("0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))");  
	    part.append(s);  
	    part.append("((");  
	    part.append("0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))");  
	    part.append(s);  
	    part.append("((");  
	    part.append("0?[1-9])|([1-2][0-9])|(30)))|(0?2");  
	    part.append(s);  
	    part.append("((0?[1-9])|(1[0-9])|(2[0-8]))))))$");  
	    return Pattern.matches(part.toString(), date);  
	}

	/**
	 * 校验时间 时间可以不写前面的0 如 9:3:1
	 * @param time 时间
	 */
	public static boolean isTime(String time) {
		if (time == null) return false;
		else return PATTERN_TIME.matcher(time).matches();
	}

	/**
	 * 严格校验身份证的格式，支持15位和18位校验
	 * @param card 
	 */
	public static boolean isIdcard(String card) {
		if (card == null)
			return false;
		int length = card.length();
		if (length == 15) {// 15位号码身份证
			if (!PATTERN_IDCARD_15.matcher(card).matches())	return false;
			if (!addressCode.containsKey(card.substring(0, 2)))	return false;
			String birthday = (new StringBuilder("19")).append(card.substring(6, 8)).append("-").append(card.substring(8, 10)).append("-").append(card.substring(10, 12)).toString();
			if (!isDate(birthday))	return false;
		} else if (length == 18) {// 18位号码身份证
			if (!PATTERN_IDCARD_18.matcher(card).matches())		return false;
			if (!addressCode.containsKey(card.substring(0, 2)))	return false;
			String birthday = (new StringBuilder(card.substring(6, 10))).append("-").append(card.substring(10, 12)).append("-").append(card.substring(12, 14)).toString();
			if (!isDate(birthday))	return false;
			int sum = 0;
			for (int i = 0; i < length - 1; i++)	sum += (card.charAt(i) - 48) * idCoefficient[i];
			char mod = idMod[sum % 11];
			if (mod != Character.toLowerCase(card.charAt(17)))	return false;
		} else {
			return false;
		}
		return true;
	}

	/**
	 * 校验IP格式
	 * 
	 * @param ip
	 * @return
	 */
	public static boolean isIP(String ip) {
		if (ip == null) return false;
		else return PATTERN_IP.matcher(ip).matches();
	}

	/**
	 * 校验是否有重复字符
	 * 
	 * @param repeat
	 * @return
	 */
	public static boolean hasRepeat(String repeat) {
		if (repeat == null) return false;
		else return PATTERN_REPEAT.matcher(repeat).matches();
	}

	public static Map<String, String> addressCode;
	public static int idCoefficient[] = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
	public static char idMod[] = { '1', '0', 'x', '9', '8', '7', '6', '5', '4', '3', '2' };

	static {
		addressCode = new HashMap<String, String>(35);
		addressCode.put("11", "\u5317\u4EAC");
		addressCode.put("12", "\u5929\u6D25");
		addressCode.put("13", "\u6CB3\u5317");
		addressCode.put("14", "\u5C71\u897F");
		addressCode.put("15", "\u5185\u8499\u53E4");
		addressCode.put("21", "\u8FBD\u5B81");
		addressCode.put("22", "\u5409\u6797");
		addressCode.put("23", "\u9ED1\u9F99\u6C5F");
		addressCode.put("31", "\u4E0A\u6D77");
		addressCode.put("32", "\u6C5F\u82CF");
		addressCode.put("33", "\u6D59\u6C5F");
		addressCode.put("34", "\u5B89\u5FBD");
		addressCode.put("35", "\u798F\u5EFA");
		addressCode.put("36", "\u6C5F\u897F");
		addressCode.put("37", "\u5C71\u4E1C");
		addressCode.put("41", "\u6CB3\u5357");
		addressCode.put("42", "\u6E56\u5317");
		addressCode.put("43", "\u6E56\u5357");
		addressCode.put("44", "\u5E7F\u4E1C");
		addressCode.put("45", "\u5E7F\u897F");
		addressCode.put("46", "\u6D77\u5357");
		addressCode.put("50", "\u91CD\u5E86");
		addressCode.put("51", "\u56DB\u5DDD");
		addressCode.put("52", "\u8D35\u5DDE");
		addressCode.put("53", "\u4E91\u5357");
		addressCode.put("54", "\u897F\u85CF");
		addressCode.put("61", "\u9655\u897F");
		addressCode.put("62", "\u7518\u8083");
		addressCode.put("63", "\u9752\u6D77");
		addressCode.put("64", "\u5B81\u590F");
		addressCode.put("65", "\u65B0\u7586");
		addressCode.put("71", "\u53F0\u6E7E");
		addressCode.put("81", "\u9999\u6E2F");
		addressCode.put("82", "\u6FB3\u95E8");
		addressCode.put("91", "\u56FD\u5916");
	}

	/** 
	 * 是否匹配
	 * 
	 * 校验类型支持
	 * $R 正则表达式校验 					EXP:   $R:\\d+.\\d
	 * $V 使用Validate默认提供的方法校验       EXP:   $V:email
	 * $M 使用自定义的静态方法校验        		EXP:   $M:org.MClass#vldP
	 * $F 对参数判断是否在类的静态成员中        	EXP:   $F:org.MClass$Status
	 * 
	 * @param vType 校验类型
	 * @param value 校验值
	 * @throws UnSupportException
	 */
	public static boolean isMatch(String vType,Object value) throws  UnSupportException{
		int subIdx=2;
		
		//正则表达式校验
		if(vType.startsWith(Type.R)){
			return ((String)value).matches(vType.substring(subIdx));
		}
		
		try {
			//自定义校验和方法校验
			if(vType.startsWith(Type.C)||vType.startsWith(Type.M)){
				Class vClass;
				String sMethodName="";
				//V校验
				if(vType.startsWith(Type.C)){
					vClass=ValidatorUtils.class;
					sMethodName="is"+WordUtils.firstToUpperCase(vType.substring(subIdx));
				//自定义方法校验
				}else{
					String[] vs=vType.substring(subIdx).split("#");
					vClass=Thread.currentThread().getContextClassLoader().loadClass(vs[0]);
					sMethodName=vs[1];
				}
				Method m = ClassUtils.getMethod(vClass,sMethodName);
				return (Boolean)m.invoke(null,value);
			//内部类枚举
			}else if(vType.startsWith(Type.E)){
				Class c=Thread.currentThread().getContextClassLoader().loadClass(vType.substring(subIdx));
					//Class.forName(vType.substring(subIdx));
				List os=new LinkedList();
				for(Field f:c.getDeclaredFields()){
					os.add(f.get(null));
				}
				return ObjectUtils.in(value, os.toArray());
			}else{
				throw new UnSupportException("校验无法识别"+vType);
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new UnSupportException(e.getMessage());
		}
	}
	
	public static class Type{
		/** 通用 */
		public final static String C	="C:";
		/** 方法 */
		public final static String M	="M:";
		/** 正则 */
		public final static String R	="R:";
		/** 成员枚举 */
		public final static String E	="E:";
	}
	
	public static class C{
		public final static String email	=Type.C+"email";
		public final static String idcard	=Type.C+"idcard";
		public final static String mobile	=Type.C+"mobile";
		public final static String tel		=Type.C+"tel";
		public final static String ip		=Type.C+"ip";
		public final static String date		=Type.C+"date";
	}
	
	/** 
	 * 静态类成员
	 * */
	public static final String VK_F="$F"; 
	
	public static boolean testVld(Object o){
		return true;
	} 
	
	public static void main(String[] args) throws Exception{
		//Class.forName("org.nobject.apps.eos.bas.model.BasUser$Lockstatus");
		//System.out.println(isMatch("R:\\d+","12"));
		System.out.println(isMatch("V:email","22222@126.com"));
		//System.out.println(isMatch("F:"+ValidatorUtils.class.getName()+"$A","S"));
		//System.out.println(isMatch("M:"+ValidatorUtils.class.getName()+"#testVld","Y"));
	}
	
	public final static class A{
		
		public final static String yes="Y";
		public final static String no="N";
	}
}
